package com.itzzy.dice.controller;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import com.itzzy.dice.bean.entity.RoomInfo;
import com.itzzy.dice.bean.req.*;
import com.itzzy.dice.bean.entity.PlayerInfo;
import com.itzzy.dice.config.CaffeineUtils;
import com.itzzy.dice.constant.RequestCode;
import com.itzzy.dice.constant.ResponseCode;
import com.itzzy.dice.utils.CommonSendUtil;
import xyz.noark.core.annotation.Autowired;
import xyz.noark.core.annotation.Controller;
import xyz.noark.core.annotation.controller.ExecThreadGroup;
import xyz.noark.core.annotation.controller.PacketMapping;
import xyz.noark.core.network.Session;
import xyz.noark.core.network.SessionAttrKey;
import xyz.noark.core.network.SessionManager;
import xyz.noark.network.SocketSession;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

import static xyz.noark.log.LogHelper.logger;

@Controller(threadGroup = ExecThreadGroup.PlayerThreadGroup)
public class DiceGameController {

    @Autowired
    private CaffeineUtils caffeineUtils;

    @PacketMapping(opcode = RequestCode.CREATE_ROOM, state = Session.State.INGAME)
    public void createRoom(SocketSession session, CreateRoomReq roomReq){
        Object isInRoom = session.attr(SessionAttrKey.valueOf("roomId")).get();
        if (ObjectUtil.isNotEmpty(isInRoom)){
            CommonSendUtil.sendErrorInfo(session, "当前用户已在房间中");
            return;
        }
        String roomId = caffeineUtils.createRoomId();

        // 创建当前用户
        PlayerInfo info = new PlayerInfo();
        info.setPlayerId(session.getPlayerId().toString());
        info.setDiceList(new ArrayList<>());
        info.setIsMaster(true);

        // 创建房间
        RoomInfo roomInfo = new RoomInfo();
        roomInfo.setRoomId(roomId);
        roomInfo.setRoomName(roomReq.getRoomName());
        roomInfo.setRoomStatus(0);
        Map<String, PlayerInfo> build = new ConcurrentHashMap<>();
        build.put(info.getPlayerId(), info);
        roomInfo.setAllPlayerMap(build);

        // 记录进缓存
        caffeineUtils.putDataCache(roomId, roomInfo);
        caffeineUtils.setPlayerRoom(info.getPlayerId(), roomId);

        // 标记当前用户已经在房间内
        session.attr(SessionAttrKey.valueOf("roomId")).set(roomId);

        // 发送房间内的信息
        session.send("", ResponseCode.ROOM_CREATE + roomInfo + "|1");
    }

    @PacketMapping(opcode = RequestCode.JOIN_ROOM, state = Session.State.INGAME)
    public void joinRoom(SocketSession session, JoinRoomReq roomReq){
        if (!caffeineUtils.contains(roomReq.getRoomId())){
            CommonSendUtil.sendErrorInfo(session, "未找到房间");
            return;
        }

        // 创建用户信息
        PlayerInfo info = new PlayerInfo();
        info.setPlayerId(session.getPlayerId().toString());
        info.setDiceList(new ArrayList<>());
        info.setIsMaster(false);
        caffeineUtils.addPlayerToCache(roomReq.getRoomId(), info);
        caffeineUtils.setPlayerRoom(info.getPlayerId(), roomReq.getRoomId());

        // 标记用户进入的房间
        session.attr(SessionAttrKey.valueOf("roomId")).set(roomReq.getRoomId());

        // 发送房间内的信息
        RoomInfo roomInfo = caffeineUtils.getObjCacheByKey(roomReq.getRoomId());
        session.send("", ResponseCode.ROOM_CREATE + roomInfo + "|0");

        // 给房间其他用户发送有人加入的信息
        for (PlayerInfo playerInfo : roomInfo.getAllPlayerMap().values()) {
            if (playerInfo.getPlayerId().equals(session.getPlayerId())){
                continue;
            }
            SessionManager.getSessionByPlayerId(playerInfo.getPlayerId()).send("", ResponseCode.JOIN_ROOM + session.getPlayerId());
        }
    }

    @PacketMapping(opcode = RequestCode.PREPARE_GAME, state = Session.State.INGAME)
    public void prepareGame(SocketSession session, PrepareGameReq gameReq){
        // 获取房间号
        String roomId = session.attr(SessionAttrKey.valueOf("roomId")).get().toString();

        // 根据房间号获取信息
        RoomInfo roomInfo = caffeineUtils.getObjCacheByKey(roomId);

        // 在观战列表中删除当前用户
        PlayerInfo info = roomInfo.getAllPlayerMap().get(session.getPlayerId().toString());
        info.setIsWatch(gameReq.getIsWatch());

        // 发送房间内的信息
//        sendRoomInfo(session, roomId);
    }

    @PacketMapping(opcode = RequestCode.START_GAME, state = Session.State.INGAME)
    public void startGame(SocketSession session){
        // 获取房间号
        String roomId = session.attr(SessionAttrKey.valueOf("roomId")).get().toString();

        // 根据房间号获取信息
        RoomInfo roomInfo = caffeineUtils.getObjCacheByKey(roomId);
        roomInfo.setRoomStatus(1);

        // 为所有准备的用户生成骰子
        String beforePlayerId = "";
        for (PlayerInfo playerInfo : roomInfo.getAllPlayerMap().values()) {
            if (playerInfo.getIsWatch() == 0) {
                playerInfo.setNextPlayerId(beforePlayerId);
                beforePlayerId = playerInfo.getPlayerId();
                playerInfo.setDiceList(randomDice());
                Session playerSession = SessionManager.getSessionByPlayerId(playerInfo.getPlayerId());
                if (playerSession == null){
                    logger.warn("开始游戏根据用户id：{} 获取到的链接为空", playerInfo.getPlayerId());
                    continue;
                }
                playerSession.send("", ResponseCode.DICE_LIST + CollectionUtil.join(playerInfo.getDiceList(), "-"));

                // 将游戏的用户加入到集合中
                roomInfo.getInGameList().add(playerInfo);
            }
        }
        roomInfo.getInGameList().get(0).setNextPlayerId(roomInfo.getInGameList().get(roomInfo.getInGameList().size() - 1).getPlayerId());

        // 发送谁第一个开始叫
        List<String> inGamePlayerIdList = roomInfo.getInGameList().stream().map(PlayerInfo::getPlayerId).collect(Collectors.toList());
        String startPlayerId = inGamePlayerIdList.get(RandomUtil.randomInt(inGamePlayerIdList.size()));
        if (ObjectUtil.isNotEmpty(roomInfo.getLastLossPlayerId()) && inGamePlayerIdList.contains(roomInfo.getLastLossPlayerId())){
            startPlayerId = roomInfo.getLastLossPlayerId();
        }
        roomInfo.setNowPlayerID(startPlayerId);
        SessionManager.getSessionByPlayerId(startPlayerId)
                .send("", ResponseCode.GAME_START + inGamePlayerIdList.size() + "-1-0");

        // 告诉其他用户第一个叫的是谁
        for (PlayerInfo playerInfo : roomInfo.getAllPlayerMap().values()) {
            Session playerSession = SessionManager.getSessionByPlayerId(playerInfo.getPlayerId());
            if (playerSession != null){
                playerSession.send("", ResponseCode.WHO_CALL + startPlayerId);
            }
        }
    }

    private List<Integer> randomDice(){
        List<Integer> resultList = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            resultList.add(RandomUtil.randomInt(1, 7));
        }
        if (CollectionUtil.distinct(resultList).size() == 5){
            return randomDice();
        }
        return resultList;
    }

    @PacketMapping(opcode = RequestCode.DICE_CHECK, state = Session.State.INGAME)
    public void diceCheckLogic(SocketSession session, DiceCheckReq checkReq){
        if (checkReq.getCheckDice() < 1 || checkReq.getCheckDice() > 6){
            CommonSendUtil.sendErrorInfo(session, "骰子类型错误");
            return;
        }

        // 获取房间号
        String roomId = session.attr(SessionAttrKey.valueOf("roomId")).get().toString();

        // 根据房间号获取信息
        RoomInfo roomInfo = caffeineUtils.getObjCacheByKey(roomId);

        // 校验大小
        if (checkDiceInfo(roomInfo, checkReq, session)){
            session.send("", ResponseCode.GAME_START
                    + (checkReq.getCheckNum() + 1) + "-" + checkReq.getCheckDice() + "-" + checkReq.getDiceType());
            return;
        }

        // 获取下一个人的id
        String nextPlayerId = roomInfo.getAllPlayerMap().get(roomInfo.getNowPlayerID()).getNextPlayerId();

        // 为所有房间里面的用户发送上次叫的
        for (PlayerInfo playerInfo : roomInfo.getAllPlayerMap().values()) {
            StringBuilder sb = new StringBuilder();
            if (checkReq.getDiceType() == 1){
                sb.append(roomInfo.getNowPlayerID()).append("-").append(roomInfo.getDiceType()).append("-")
                        .append(roomInfo.getCheckNum()).append("-").append(roomInfo.getCheckDice()).append("-").append(nextPlayerId);
            }else {
                sb.append(roomInfo.getNowPlayerID()).append("-").append(roomInfo.getDiceType()).append("-")
                        .append(roomInfo.getNormalCheckNum()).append("-").append(roomInfo.getNormalCheckDice()).append("-").append(nextPlayerId);
            }
            Session player = SessionManager.getSessionByPlayerId(playerInfo.getPlayerId());
            if (player != null){
                player.send("", ResponseCode.DICE_CALL + sb);
            }
        }
        roomInfo.setBeforePlayerId(roomInfo.getNowPlayerID());
        roomInfo.setNowPlayerID(nextPlayerId);

        // 通知下一个叫的
        SessionManager.getSessionByPlayerId(nextPlayerId)
                .send("", ResponseCode.GAME_START
                        + (checkReq.getCheckNum() + 1) + "-" + checkReq.getCheckDice() + "-" + checkReq.getDiceType());
    }

    /**
     * 前端 如果选择的骰子是1 是  需要讲 diceType 修改为栽
     */
    private boolean checkDiceInfo(RoomInfo roomInfo, DiceCheckReq checkReq, SocketSession session){
        // 校验刚开始
        if (roomInfo.isFirst()){
            roomInfo.setFirst(false);
            // 如果选择的是栽
            if (checkReq.getDiceType() == 1){
                roomInfo.setDiceType(1);
                roomInfo.setCheckDice(checkReq.getCheckDice());
                roomInfo.setCheckNum(checkReq.getCheckNum());
            }else {
                roomInfo.setDiceType(0);
                roomInfo.setNormalCheckNum(checkReq.getCheckNum());
                roomInfo.setNormalCheckDice(checkReq.getCheckDice());
            }
        }else {
            // 第二次叫骰
            // 如果叫的是栽 并且  上次叫的也是栽
            if (checkReq.getDiceType() == 1 && roomInfo.getDiceType() == 1){
                if (checkDoubleZai(roomInfo, checkReq, session)){
                    return true;
                }

                roomInfo.setDiceType(1);
                roomInfo.setCheckDice(checkReq.getCheckDice());
                roomInfo.setCheckNum(checkReq.getCheckNum());
            }

            // 如果叫的是栽 但是  上次叫的是普通
            if (checkReq.getDiceType() == 1 && roomInfo.getDiceType() == 0){
                if (roomInfo.getCheckDice() != 0){
                    // 如果之前叫过栽
                    if (checkDoubleZai(roomInfo, checkReq, session)){
                        return true;
                    }
                }

                // 如果本次栽的数量 <  上次普通数量 / 2
                if (checkReq.getCheckNum() * 2 < roomInfo.getNormalCheckNum()){
                    CommonSendUtil.sendErrorInfo(session, "上局普通叫号为【" + roomInfo.getNormalCheckNum() + "个" + roomInfo + "】，本次栽的数量 x2 必须比上次多");
                    return true;
                }

                roomInfo.setDiceType(1);
                roomInfo.setCheckDice(checkReq.getCheckDice());
                roomInfo.setCheckNum(checkReq.getCheckNum());
            }

            // 如果叫的是普通  并且  上次叫的也是普通
            if (checkReq.getDiceType() == 0 && roomInfo.getDiceType() == 0){
                if (checkDoubleNormal(roomInfo, checkReq, session)){
                    return true;
                }

                roomInfo.setDiceType(0);
                roomInfo.setNormalCheckDice(checkReq.getCheckDice());
                roomInfo.setNormalCheckNum(checkReq.getCheckNum());
            }

            // 如果叫的是普通  但是  上次叫的也是栽
            if (checkReq.getDiceType() == 0 && roomInfo.getDiceType() == 1){
                if (roomInfo.getNormalCheckDice() != 0){
                    // 如果之前叫过普通
                    if (checkDoubleNormal(roomInfo, checkReq, session)){
                        return true;
                    }
                }

                if (checkReq.getCheckNum() < roomInfo.getCheckNum() * 2){
                    CommonSendUtil.sendErrorInfo(session, "上局栽叫号为【" + roomInfo.getNormalCheckNum() + "个" + roomInfo + "】，本次普通数量必须比上次x2多");
                    return true;
                }
                roomInfo.setDiceType(0);
                roomInfo.setNormalCheckDice(checkReq.getCheckDice());
                roomInfo.setNormalCheckNum(checkReq.getCheckNum());
            }
        }
        return false;
    }

    private boolean checkDoubleZai(RoomInfo roomInfo, DiceCheckReq checkReq, SocketSession session){
        // 如果本次和上次叫的一样  并  数量小于等于上次的
        if (checkReq.getCheckDice() == roomInfo.getCheckDice() && checkReq.getCheckNum() <= roomInfo.getCheckNum()){
            CommonSendUtil.sendErrorInfo(session, "上局栽叫号为【" + roomInfo.getCheckNum() + "个" + roomInfo.getCheckDice() + "】，必须比上局多一个");
            return true;
        }
        // 如果本次叫的 <= 上次的  并且  本次数量  <= 上次数量
        if (checkReq.getCheckDice() <= roomInfo.getCheckDice() && checkReq.getCheckNum() <= roomInfo.getCheckNum()){
            CommonSendUtil.sendErrorInfo(session, "上局栽叫号为【" + roomInfo.getCheckNum() + "个" + roomInfo.getCheckDice() + "】，必须比上局多一个");
            return true;
        }

        // 如果本次叫的 > 上次的大  但是  本次数量  < 上次数量
        if (checkReq.getCheckDice() > roomInfo.getCheckDice() && checkReq.getCheckNum() < roomInfo.getCheckNum()){
            CommonSendUtil.sendErrorInfo(session, "上局栽叫号为【" + roomInfo.getCheckNum() + "个" + roomInfo.getCheckDice() + "】，必须比上局多一个或相同");
            return true;
        }
        return false;
    }

    private boolean checkDoubleNormal(RoomInfo roomInfo, DiceCheckReq checkReq, SocketSession session){
        // 如果本次和上次叫的一样  并  数量小于等于上次的
        if (checkReq.getCheckDice() == roomInfo.getNormalCheckDice() && checkReq.getCheckNum() <= roomInfo.getNormalCheckNum()){
            CommonSendUtil.sendErrorInfo(session, "上局普通叫号为【" + roomInfo.getCheckNum() + "个" + roomInfo.getCheckDice() + "】，必须比上局多一个");
            return true;
        }
        // 如果本次叫的 <= 上次的  并且  本次数量  <= 上次数量
        if (checkReq.getCheckDice() <= roomInfo.getNormalCheckDice() && checkReq.getCheckNum() <= roomInfo.getNormalCheckNum()){
            CommonSendUtil.sendErrorInfo(session, "上局普通叫号为【" + roomInfo.getCheckNum() + "个" + roomInfo.getCheckDice() + "】，必须比上局多一个");
            return true;
        }

        // 如果本次叫的 > 上次的大  但是  本次数量  < 上次数量
        if (checkReq.getCheckDice() > roomInfo.getNormalCheckDice() && checkReq.getCheckNum() < roomInfo.getNormalCheckNum()){
            CommonSendUtil.sendErrorInfo(session, "上局普通叫号为【" + roomInfo.getCheckNum() + "个" + roomInfo.getCheckDice() + "】，必须比上局多一个或相同");
            return true;
        }
        return false;
    }

    @PacketMapping(opcode = RequestCode.OPEN_DICE, state = Session.State.INGAME)
    public void openTheDice(SocketSession session){
        // 获取房间号
        String roomId = session.attr(SessionAttrKey.valueOf("roomId")).get().toString();

        // 根据房间号获取信息
        RoomInfo roomInfo = caffeineUtils.getObjCacheByKey(roomId);

        // 重新计算每个人的骰子
        for (PlayerInfo playerInfo : roomInfo.getInGameList()) {
            List<Integer> diceItemList = playerInfo.getDiceList();

            ArrayList<Integer> distinctList = CollectionUtil.distinct(diceItemList);
            // 如果是豹子，则可以增加一个数
            if (distinctList.size() == 1){
                diceItemList.add(diceItemList.get(0));
            }
            // 如果开的是普通，只有 1 与 其他数  则也是豹子
            if (roomInfo.getDiceType() == 0 && distinctList.size() == 2 && distinctList.contains(1)){
                Integer otherDice = distinctList.stream().filter(i -> i != 1).collect(Collectors.toList()).get(0);
                diceItemList.add(otherDice);
            }

            playerInfo.setDiceList(diceItemList);
        }

        // 汇总所有的骰子
        List<List<Integer>> allDiceList = roomInfo.getInGameList().stream().map(PlayerInfo::getDiceList).collect(Collectors.toList());
        Map<Integer, List<Integer>> diceMap = allDiceList.stream().flatMap(Collection::stream).collect(Collectors.groupingBy(Function.identity()));
        int checkNum;
        int checkDice;
        if (roomInfo.getDiceType() == 0){
            checkNum = roomInfo.getNormalCheckNum();
            checkDice = roomInfo.getNormalCheckDice();
        }else {
            checkNum = roomInfo.getCheckNum();
            checkDice = roomInfo.getCheckDice();
        }

        // 区分胜负 如果叫的数量大于等于所有的数量  则前一个胜利
        PlayerInfo playerInfo;
        int checkDiceNum = 0;
        if (diceMap.containsKey(checkDice)) {
            checkDiceNum = diceMap.get(checkDice).size();
        }
        int checkDiceNumOne = 0;
        if (diceMap.containsKey(1)){
            checkDiceNumOne = diceMap.get(1).size();
        }

        if (roomInfo.getDiceType() == 1) {
            if (checkNum > checkDiceNum) {
                playerInfo = roomInfo.getAllPlayerMap().get(roomInfo.getBeforePlayerId());
            } else {
                playerInfo = roomInfo.getAllPlayerMap().get(roomInfo.getNowPlayerID());
            }
        }else {
            if (checkNum > checkDiceNum + checkDiceNumOne) {
                playerInfo = roomInfo.getAllPlayerMap().get(roomInfo.getBeforePlayerId());
            } else {
                playerInfo = roomInfo.getAllPlayerMap().get(roomInfo.getNowPlayerID());
            }
        }
        playerInfo.setGrade(playerInfo.getGrade() + 1);

        // 返回所有骰子信息
        String diceList = roomInfo.getInGameList().stream().map(info -> info.getPlayerId() + "-" + CollectionUtil.join(info.getDiceList(), "-")).collect(Collectors.joining("="));
        roomInfo.getAllPlayerMap().values().forEach(info -> {
            Session playSession = SessionManager.getSessionByPlayerId(info.getPlayerId());
            if (playSession != null) {
                playSession.send("",
                        ResponseCode.GRADE_INFO + playerInfo.getPlayerId() + "-" + playerInfo.getGrade() + "-" + roomInfo.getDiceType() + "|" + diceList);
            }
        });

        // 重置所有数据
        roomInfo.setLastLossPlayerId(playerInfo.getPlayerId());
        roomInfo.setFirst(true);
        roomInfo.setDiceType(0);
        roomInfo.setCheckNum(0);
        roomInfo.setCheckDice(0);
        roomInfo.setNormalCheckNum(0);
        roomInfo.setNormalCheckDice(0);
        roomInfo.setInGameList(new ArrayList<>());
    }


    @PacketMapping(opcode = RequestCode.SEND_MSG, state = Session.State.INGAME)
    public void sendMsg(SocketSession session, SendMsgReq msgReq){
        // 获取房间号
        String roomId = session.attr(SessionAttrKey.valueOf("roomId")).get().toString();

        // 根据房间号获取信息
        RoomInfo roomInfo = caffeineUtils.getObjCacheByKey(roomId);

        roomInfo.getAllPlayerMap().values().forEach(info -> {
            SessionManager.getSessionByPlayerId(info.getPlayerId()).send("",
                    ResponseCode.SEND_MSG + session.getPlayerId() + "|" + msgReq.getMsg());
        });
    }

    @PacketMapping(opcode = RequestCode.KICK_ASS, state = Session.State.INGAME)
    public void kickAss(SocketSession session, UsernameReq usernameReq){
        String roomId = caffeineUtils.getRoomIdByPlayerId(usernameReq.getUserName());

        // 删除房间内的缓存信息
        RoomInfo roomInfo = caffeineUtils.getObjCacheByKey(roomId);
        roomInfo.getAllPlayerMap().remove(usernameReq.getUserName());

        // 删除用户房间信息缓存
        caffeineUtils.removePlayerRoom(usernameReq.getUserName());

        // 为房间里面的其他人发送消息
        roomInfo.getAllPlayerMap().values().forEach(info -> {
            Session playSession = SessionManager.getSessionByPlayerId(info.getPlayerId());
            if (playSession != null) {
                playSession.send("", ResponseCode.KICK_ASS + usernameReq.getUserName());
            }
        });


        Session kickSession = SessionManager.getSessionByPlayerId(usernameReq.getUserName());
        if (kickSession != null){
            session.attr(SessionAttrKey.valueOf("roomId")).set(null);
        }
    }


}
